package com.tzl.mfwl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 一些字符串处理的工具类
 *
 * @author tang zhilei
 * @date 2022/07/26 14:01
 */
public class StringUtils {
    /**
     * 该方法结合正向匹配和逆向匹配的结果，得到分词的最终结果
     *
     * @param content 原始内容
     * @param dict 分词字典
     * @return 分词结果
     */
    public static List<String> analyze(String content, Collection<String> dict) {
        // 忽略匹配大小写
        content = content.toUpperCase();
        Map<String, String> dictMap =
                dict.stream()
                        .collect(Collectors.toMap(String::toUpperCase, item -> item, (s, s2) -> s));
        Set<String> dicts = dictMap.keySet();
        int maxLen = getMaxLen(dicts);
        // 将关键词还原
        List<String> fmmList =
                fmm(content, dicts, maxLen).stream().map(dictMap::get).collect(Collectors.toList());
        List<String> bmmList =
                bmm(content, dicts, maxLen).stream().map(dictMap::get).collect(Collectors.toList());
        // 如果两个分词方法都只有一个结果,则返回字符最多的那个
        if ((fmmList.size() == bmmList.size()) && fmmList.size() == 1) {
            return fmmList.get(0).length() < bmmList.get(0).length() ? bmmList : fmmList;
        }
        // 如果两个分词结果词数不同, 则取分词数量较少的那个
        if (fmmList.size() != bmmList.size()) {
            return fmmList.size() > bmmList.size() ? bmmList : fmmList;
        }
        // 结果词数相同
        // 如果分词结果相同,没有歧义,可返回任意一个结果
        int fSingle = 0, bSingle = 0;
        boolean isSame = true;
        for (int i = 0; i < fmmList.size(); i++) {
            if (!fmmList.get(i).equals(bmmList.get(i))) {
                isSame = false;
            }
            if (fmmList.get(i).length() == 1) {
                fSingle += 1;
            }
            if (bmmList.get(i).length() == 1) {
                bSingle += 1;
            }
        }
        if (isSame) {
            return fmmList;
        } else {
            // 不同,返回单字较少的那个结果
            return bSingle >= fSingle ? fmmList : bmmList;
        }
    }

    /**
     * 获取最长的那个关键词
     *
     * @param dict
     * @return
     */
    private static int getMaxLen(Collection<String> dict) {
        int maxLen = 0;
        for (String s : dict) {
            if (s != null) {
                if (s.length() > maxLen) {
                    maxLen = s.length();
                }
            }
        }
        return maxLen;
    }

    /**
     * 前向分词算法
     *
     * @param content 待分词字符串
     * @param dict 分词词典
     * @param maxLen 单次截取子串去匹配字典的长度,必须大于字典中最长的那个关键词
     * @return 分词结果
     */
    public static List<String> fmm(
            final String content, final Collection<String> dict, final int maxLen) {
        int lenParse = content.length();
        List<String> fmmList = new ArrayList<>();
        int i = 0;
        int j;
        while (i < lenParse) {
            int end = i + maxLen;
            if (end >= lenParse) {
                end = lenParse;
            }
            String parseSub = content.substring(i, end);
            for (j = parseSub.length(); j >= 0; j--) {
                String key = parseSub.substring(0, j);
                if (dict.contains(key)) {
                    fmmList.add(key);
                    i += key.length() - 1;
                    break;
                }
            }
            i += 1;
        }
        return fmmList;
    }

    /**
     * 后向分词算法
     *
     * @param content
     * @param dict
     * @return
     */
    public static List<String> bmm(
            final String content, final Collection<String> dict, final int maxLen) {
        List<String> bmmList = new ArrayList<>();
        int i = content.length();
        int j;
        while (i > 0) {
            int start = i - maxLen;
            if (start < 0) {
                start = 0;
            }
            String parseSub = content.substring(start, i);
            for (j = 0; j < parseSub.length(); j++) {
                String key = parseSub.substring(j);
                if (dict.contains(key)) {
                    bmmList.add(0, key);
                    i -= key.length() - 1;
                    break;
                }
            }
            i -= 1;
        }
        return bmmList;
    }
}
